#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/rcupdate.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("MAGC");
MODULE_DESCRIPTION("Magc Module Compile");

#define MAX_KTHREAD     10
static struct task_struct *threads[MAX_KTHREAD];

//定义被保护的数据

struct our_data{
    int count1;
    int count2;
    struct rcu_head head;//内嵌此结构体是为了配合使用call_rcu()
};

static void rcu_free(struct rcu_head *rhead)
{

    struct our_data *data;
    //由内嵌的结构体指针，根据偏移量，得到整体数据结构体的指针
    data = container_of(rhead , struct our_data , head);
    kfree(data);
}
//定义一个Ｂｉｔｍａｐ
static unsigned long reader_bitmap = 0;

static void set_reader_number(int reader)
{
        reader_bitmap = (1 << reader);
}
static struct our_data my_data;
// RCU是专门用来保护指针的
static struct our_data __rcu *pmy_data = &my_data;
static void show_data(void)
{
    printk("my_data.count1 = %d ; my_data.count2 = %d\n",my_data.count1 , my_data.count2);
}

/**
 * @brief 读者使用ＲＣＵ来控制
 */
static void reader_do(long num)
{
    struct our_data *mydata;
    //rcu读锁
    rcu_read_lock();
    //通过RCU保护的指针一定要通过此接口来使用
    mydata = rcu_dereference(pmy_data);
    printk("reader %ld reading out_data: count1=%d ,count2=%d\n",num , mydata->count1,mydata->count2);
    rcu_read_unlock(); 

    msleep(10);
}

/**
 * @brief 写者需要开发者自己实现同步控制
 * 这里只定义一个写者，暂时不用控制
 */
static void write_do(long num)
{

    //这里让写者稍等会儿再更新数据
    msleep(200);
    struct our_data *mydata ; 
    struct our_data *tmp;
    //第一步：申请新的内存来作为原数据的备份，进行修改
    mydata = kmalloc(sizeof(*mydata),GFP_KERNEL);
    if(!mydata)
        return;
    memcpy(mydata , pmy_data , sizeof(*mydata));
    mydata->count1 ++ ;
    mydata->count2 += 10;
    printk("writer %ld is update count1=%d,count2=%d ...\n",num , mydata->count1,mydata->count2);
    tmp = pmy_data;//暂存旧指针，用来释放旧内存
    //第二步：修改完毕后，使用rcu_assign_pointer接口来更新指针，使原指针切换到这个新修改的数据内存上
    rcu_assign_pointer(pmy_data,mydata);
    //第三步：释放旧内存空间，但要注意要安全地释放，即让所有读者正常读完后再释放
    if(!tmp )
    {
        call_rcu(&(tmp->head) , rcu_free);
        printk("writer %ld is kfree ...\n",num);
    }
}

/**
 * @brief 在线程运行过程中，让它等待，直到让它stop为止
 *
 * @param data
 * 这里根据参数来指定该线程做读操作还是写操作
 *
 * @return 
 */
static int thread_do(void *data)
{
    long i = (long)data;
    int writer = reader_bitmap & (1 << i);
    printk("run .... %ld is a %s....\n",i,writer?"writer":"reader");
    //所有线程在接收到退出信息前一直运行　
    while(!kthread_should_stop())
    {
        if(writer)
        {
            write_do(i);
        }
        else
            reader_do(i);
        msleep(10);
    }
    return 0;
}

static int create_thread(void)
{
    int i;
    for (i = 0; i < MAX_KTHREAD; ++i) {
        struct task_struct *thread;
        thread = kthread_run(thread_do ,(void *)(long)i,"thread-%d",i);
        if (IS_ERR(thread))
            return -1;
        threads[i] = thread;
    }
    return 0;
}

/**
 * @brief 在此处按顺序去停止线程时，有些线程有可能已经结束退出了，会产生非法指针访问的情况
 * 所以，在线程运行过程中让它等待一段时间，直到让它stop为止
 */
static void cleanup_threads(void)
{
    int i;
    for (i = 0; i < MAX_KTHREAD; ++i) {
        if(threads[i])
            kthread_stop(threads[i]);
    }
}
static int minit(void)
{
    printk("call %s \n",__FUNCTION__);
    set_reader_number(5);
    printk("reader is %ld",reader_bitmap);
    if(create_thread())
        goto err;
    return 0;
err:
    cleanup_threads();
    return -1;
}

static void mexit(void)
{

    printk("call %s \n",__FUNCTION__);
    show_data();
    cleanup_threads();
}
module_init(minit);
module_exit(mexit);
